import { fabric } from 'fabric';

export const RoundTriangle = fabric.util.createClass(fabric.Triangle, {
  type: 'roundTriangle',
  stateProperties: fabric.Object.prototype.stateProperties!.concat('radius'),
  cacheProperties: fabric.Object.prototype.cacheProperties!.concat('radius'),

  initialize: function (options) {
    options || (options = {});
    options.noScaleCache = false;

    this.callSuper('initialize', options);
    this.set('radius', options.radius || 0);
  },

  toObject: function () {
    return fabric.util.object.extend(this.callSuper('toObject'), {
      radius: this.get('radius')
    });
  },

  _render: function (ctx: CanvasRenderingContext2D) {
    const { scaleX, scaleY } = this;
    // 这里直接取width和height，而没有减去边框宽度，是因为fabricjs里自带的shape都是没有减边框的，所以这里需要保持边框展示逻辑一致
    const width = this.width * scaleX;
    const height = this.height * scaleY;
    const widthBy2 = width / 2;
    const heightBy2 = height / 2;

    // 先计算最大内切圆半径
    /**
     * 若面积为S，三边分别为a、b、c，周长为p=a+b+c，内切圆半径为r，
     * 连接内心与三个顶点，可将三角形分成三个小三角形，
     * 所以 S=ra/2+rb/2+rc/2=r(a+b+c)/2=rp/2，
     * 因此，r=2S/p 。
     * 即 内切圆半径等于三角形面积的2倍除以周长。
     */
    const l = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height, 2)); // 三角形腰长
    const perimeter = 2 * l + width; // 周长 = 2腰长 + 底长
    const maxR = (width * height) / perimeter; // 内切圆半径，圆角半径不能超过此值

    const radius = Math.min(this.radius, maxR) || 0;

    // 顶点
    const topPoint = [0, -heightBy2] as const;
    // 右下点
    const rightBottomPoint = [widthBy2, heightBy2] as const;
    // 左下点
    const leftBottomPoint = [-widthBy2, heightBy2] as const;

    ctx.scale(1 / scaleX, 1 / scaleY);

    ctx.beginPath();

    // 从底部中心点开始
    ctx.moveTo(0, heightBy2);

    // 从底部中心点，到左下点，再到顶点，为切线，画左下圆弧
    // 参考：https://www.runoob.com/tags/canvas-arcto.html
    // 传入的点是三角形的左下点 + 顶点
    ctx.arcTo(...leftBottomPoint, ...topPoint, radius);

    // 从左下圆弧终点，到顶点，再到右下点，为切线，画顶部圆弧
    ctx.arcTo(...topPoint, ...rightBottomPoint, radius);

    // 从顶部圆弧终点，到右下点，再到左下点，为切线，画右下圆弧
    ctx.arcTo(...rightBottomPoint, ...leftBottomPoint, radius);

    ctx.closePath();

    this._renderPaintInOrder(ctx);

    ctx.scale(1, 1);
  }
});

declare module 'fabric' {
  namespace fabric {
    class RoundTriangle {
      static fromObject(
        object: fabric.Object,
        callback: Function
      ): RoundTriangle;
    }
  }
}
